/*
 * MIT License
 *
 * Copyright (c) 2023 北京凯特伟业科技有限公司
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
package com.je.meta.service;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.poi.excel.WorkbookUtil;
import com.google.common.collect.Maps;
import com.je.common.base.DynaBean;
import com.je.common.base.exception.PlatformException;
import com.je.common.base.exception.PlatformExceptionEnum;
import com.je.common.base.service.CommonService;
import com.je.common.base.service.MetaService;
import com.je.common.base.service.rpc.BeanService;
import com.je.common.base.service.rpc.DocumentInternalRpcService;
import com.je.common.base.util.*;
import com.je.ibatis.extension.conditions.ConditionsWrapper;
import com.je.meta.model.ExcelParamVo;
import com.je.meta.model.ExcelReturnVo;
import com.je.meta.rpc.excel.ExcelRpcService;
import org.apache.poi.ss.usermodel.*;
import org.springframework.beans.factory.annotation.Autowired;
import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @program: jecloud-meta
 * @author: LIULJ
 * @create: 2021/8/9
 * @description:
 */
public abstract class AbstractExcelServiceImpl implements ExcelService {

    @Autowired
    private MetaService metaService;
    @Autowired
    private CommonService commonService;
    @Autowired
    private ExcelRpcService excelRpcService;
    @Autowired
    private DocumentInternalRpcService documentInternalRpcService;

    public InputStream expUseTemplate(String sheetId, List<DynaBean> datas) throws IOException {
        //校验模板是否存在
        List<Map<String, Object>> list = excelRpcService.funcTemplateListWithComustomer(sheetId, "");
        if (list == null || list.isEmpty()) {
            throw new PlatformException("模板未找到,无法导出!", PlatformExceptionEnum.JE_CORE_EXCEL_EXP_ERROR);
        }
        //校验模板文件是否存在
        String excelGroupFile = list.get(0).get("EXCELGROUP_FILE").toString();
        String fileKey = excelGroupFile.split("\\*")[excelGroupFile.split("\\*").length - 1];
        if (StringUtil.isEmpty(fileKey) || !documentInternalRpcService.exists(fileKey)) {
            throw new PlatformException("模板未找到,无法导出!", PlatformExceptionEnum.JE_CORE_EXCEL_EXP_ERROR);
        }
        //查询列配置
        List<DynaBean> fields = metaService.select("JE_CORE_EXCELFIELD",ConditionsWrapper.builder().eq("JE_CORE_EXCELSHEET_ID",sheetId).isNotNull("EXCELFIELD_COLUMN").ne("EXCELFIELD_COLUMN",""));
        if (fields.isEmpty()) {
            throw new PlatformException("未找到模板列配置,无法导出!", PlatformExceptionEnum.JE_CORE_EXCEL_EXP_ERROR);
        }

        //获取模板文件流
        File file = documentInternalRpcService.readFile(fileKey);

        //缓存为临时文件
        String packageTempFilePath = SystemProperty.getRootPath() + "/excel_temp/%s";
        String tempFilePath = String.format(packageTempFilePath, System.currentTimeMillis(), file.getName());
        FileUtil.writeFromStream(FileUtil.getInputStream(file), tempFilePath);
        File tempFile = new File(tempFilePath);

        //开始位置
        int sheetIndex = Integer.parseInt(list.get(0).get("SY_ORDERINDEX").toString()) - 1;
        int startRow = Integer.parseInt(list.get(0).get("STARTROWS").toString()) - 1;

        //创建excel操作对象
        Workbook book = WorkbookUtil.createBook(tempFile);
        Sheet sheet = book.getSheetAt(sheetIndex);

        //获取默认样式
        int defaultColumnWidth = sheet.getDefaultColumnWidth();
        float defaultRowHeightInPoints = sheet.getDefaultRowHeightInPoints();
        Row firstRow = sheet.getRow(startRow);
        CellStyle defRowStyle = book.createCellStyle();

        CellStyle firstColumnStyle = null;
        Map<Integer, CellStyle> defaultcolumnStyles = Maps.newHashMap();
        if (firstRow != null) {
            defRowStyle = firstRow.getRowStyle();
            defaultRowHeightInPoints = firstRow.getHeightInPoints();
            //获取列默认样式
            int columnNum = firstRow.getPhysicalNumberOfCells();
            for (int i = 0; i < columnNum; i++) {
                Cell cell = firstRow.getCell(i);
                if (cell != null) {
                    //清空示例数据
                    cell.setCellValue("");
                    //获取样式
                    CellStyle cellStyle = cell.getCellStyle();
                    if (cellStyle != null) {
                        defaultcolumnStyles.put(i, cellStyle);
                        if (firstColumnStyle == null) {
                            firstColumnStyle = book.createCellStyle();
                            firstColumnStyle.cloneStyleFrom(cellStyle);
                            //单元格默认设置文本
                            firstColumnStyle.setDataFormat(book.createDataFormat().getFormat("@"));
                        }
                    }
                }
            }
        }


        //field转换map,key为列对应的数字， A -> 0; Z -> 25; AA -> 26;
        Map<Integer, DynaBean> fieldMap = fields.stream().filter(p -> StringUtil.isNotEmpty(p.getStr("EXCELFIELD_COLUMN")))
                .collect(Collectors.toMap(p -> excelColStrToNum(p.getStr("EXCELFIELD_COLUMN")), column -> {
                    //设置 RESOURCEFIELD_XTYPE,RESOURCEFIELD_CONFIGINFO  方便调用公共方法
                    column.setStr("RESOURCEFIELD_XTYPE", column.getStr("EXCELFIELD_XTYPE"));
                    column.setStr("RESOURCEFIELD_CONFIGINFO", column.getStr("EXCELFIELD_CONFIGINFO"));
                    return column;
                }));

        //获取字典信息
        Map<String, Map<String, String>> ddInfos = excelRpcService.buildDicDatas(fields);

        //填充数据
        for (int i = 0; i < datas.size(); i++) {

            //行号
            int rowNum = startRow + i;

            //获取行
            Row row = sheet.getRow(rowNum);
            if (row == null) {
                row = sheet.createRow(rowNum);
                row.setHeightInPoints(defaultRowHeightInPoints);
                if (defRowStyle != null) {
                    row.setRowStyle(defRowStyle);
                }
            }
            //数据
            DynaBean data = datas.get(i);
            //填充列
            for (Map.Entry<Integer, DynaBean> entry : fieldMap.entrySet()) {

                int colIndex = entry.getKey();
                DynaBean column = entry.getValue();

                Cell cell = row.getCell(colIndex);
                if (cell == null) {
                    cell = row.createCell(colIndex);
                    CellStyle cellStyle = defaultcolumnStyles.get(colIndex);
                    if (cellStyle != null) {
                        cell.setCellStyle(cellStyle);
                    } else if (firstColumnStyle != null) {
                        cell.setCellStyle(firstColumnStyle);
                    }
                }
                String columnCode = column.getStr("EXCELFIELD_CODE");
                String value = data.getStr(columnCode, "");
                String xtype = column.getStr("EXCELFIELD_XTYPE");
                if (ArrayUtils.contains(new String[]{"rgroup", "cgroup", "cbbfield"}, xtype)) {
                    //获取字典对应值
                    String ddValue = getDdValue(ddInfos, column, value);
                    cell.setCellValue(ddValue);
                } else if ("rownumberer".equalsIgnoreCase(xtype)) {
                    cell.setCellValue(i + 1);
                } else {
                    cell.setCellValue(value);
                }
            }
        }

        try {
            // 写出数据到流
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            book.write(out);
            out.flush();
            ByteArrayInputStream inputStream = new ByteArrayInputStream(out.toByteArray());
            IoUtil.close(out);
            IoUtil.close(book);
            return inputStream;
        } catch (Exception e) {
            e.printStackTrace();
        }

        return FileUtil.getInputStream(tempFile);
    }


    /**
     * excel列转数字
     * A -> 0; Z -> 25; AA -> 26;
     *
     * @param colStr excel列
     * @return int
     */
    public int excelColStrToNum(String colStr) {
        int num = 0;
        int result = -1;
        for (int i = 0; i < colStr.length(); i++) {
            char ch = colStr.charAt(colStr.length() - i - 1);
            num = (int) (ch - 'A' + 1);
            num *= Math.pow(26, i);
            result += num;
        }
        return result;
    }

    public int columnWidth(DynaBean column) {
        Integer width = column.getInt("RESOURCECOLUMN_WIDTH", 80);
        //暂定比例  按照  1个字符是6像素计算
        Double w = MathExtend.divide(width / (double) 6, 1, 0);
        //将像素换算成excel宽度      此方法参数意思是：单位的1/256th字符宽度
        return Integer.parseInt(new java.text.DecimalFormat("0").format(w));
    }

    /**
     * 获取字典字段的code的name值
     *
     * @param ddInfos
     * @param column
     * @param codeValue
     * @return
     */
    public String getDdValue(Map<String, Map<String, String>> ddInfos, DynaBean column, String codeValue) {
        String xtype = column.getStr("RESOURCEFIELD_XTYPE");
        //多选框
        Map<String, String> ddItems = null;
        String configInfo = column.getStr("RESOURCEFIELD_CONFIGINFO");
        if (StringUtil.isNotEmpty(configInfo)) {
            String ddCode = configInfo.split(ArrayUtils.SPLIT)[0];
            ddItems = ddInfos.get(ddCode);
        }
        if (ddItems != null) {
            if ("cgroup".equals(xtype)) {
                if (StringUtil.isNotEmpty(codeValue)) {
                    String[] codeValues = codeValue.split(ArrayUtils.SPLIT);
                    for (Integer i = 0; i < codeValues.length; i++) {
                        if (StringUtil.isNotEmpty(codeValues[i]) && ddItems.get(codeValues[i]) != null) {
                            codeValues[i] = ddItems.get(codeValues[i]);
                        }
                    }
                    return StringUtil.buildSplitString(codeValues, ",");
                } else {
                    return codeValue;
                }
            } else {
                if (StringUtil.isNotEmpty(codeValue) && ddItems.get(codeValue) != null) {
                    return ddItems.get(codeValue);
                } else {
                    return codeValue;
                }
            }
        } else {
            return codeValue;
        }
    }

    @Override
    public List<ExcelReturnVo> doData(DynaBean group, List<DynaBean> sheets, Map<String, List<DynaBean>> results, Map<String,Object> ddSet, HttpServletRequest request, Map<String, Map<String, DynaBean>> allFieldInfos) {
        List<ExcelReturnVo> returnMsgs = new ArrayList<ExcelReturnVo>();
        String allType = group.getStr("DATA_TYPE");
        for (DynaBean sheet : sheets) {
            int sheetOrder = sheet.getInt("SY_ORDERINDEX", 0) - 1;
            String key = "sheet" + sheetOrder;
            String sheetName = sheet.getStr("SHEETNAME");
            String tableCode = sheet.getStr("EXCELSHEET_TABLECODE", "");
            int startRow = sheet.getInt("STARTROWS", 0);
            List<DynaBean> datas = results.get(key);
            Map<String, DynaBean> fieldCodeInfos = allFieldInfos.get(key);
            //声明字段信息
            if ("SYS".equals(allType)) {
                String clType = sheet.getStr("DATA_TYPE");
                String clcl = sheet.getStr("DATA_CLTYPE");
                if ("BEAN".equals(clType) && "ALL".equals(clcl)) {
                    returnMsgs.add(executeLogic("BEAN", "", "", "", "", "DATA", sheet.getStr("DATA_BEAN"), sheet.getStr("DATA_METHOD"), ddSet, new ExcelParamVo(request, results, datas, sheetOrder, sheetName, "SHEET_DATA", fieldCodeInfos)));
                } else {
                    int nowRow = startRow + 1;
                    List<String> unSqls;
                    for (DynaBean data : datas) {
                        //系统默认插入
                        if ("SYS".equals(clType)) {
                            if (StringUtil.isNotEmpty(data.getStr(BeanService.KEY_TABLE_CODE))) {
                                //是否修改
                                if ("1".equals(sheet.getStr("DATA_UPDATE"))) {
                                    String updateCodes = sheet.getStr("EXCELSHEET_WYZDBM");
                                    unSqls = new ArrayList<String>();
                                    if (StringUtil.isNotEmpty(updateCodes)) {
                                        for (String fieldCode : updateCodes.split(",")) {
                                            unSqls.add(" " + fieldCode + "='" + data.getStr(fieldCode) + "'");
                                        }
                                    }
                                    List<DynaBean> dbLists = metaService.select(tableCode,ConditionsWrapper.builder().apply(" AND " + StringUtil.buildSplitString(ArrayUtils.getArray(unSqls), " AND ")));
                                    if (dbLists.size() == 1) {
                                        data.set(data.getStr(BeanService.KEY_PK_CODE), dbLists.get(0).getStr(data.getStr(BeanService.KEY_PK_CODE)));
                                        commonService.buildModelModifyInfo(data);
                                        metaService.update(data);
                                    } else if (dbLists.size() > 1) {
                                        //failure++;
                                        //failures.add("第"+row+"行：数据根据唯一字段查询出重复数据。");
                                        continue;
                                    } else {
                                        metaService.insert(data);
                                    }
                                } else {
                                    metaService.insert(data);
                                }
                                //插入
                            }
                            //调用存储过程插入
                        } else if ("PROCEDURE".equals(clType)) {
                            returnMsgs.add(executeLogic("PROCEDURE", "", sheet.getStr("DATA_DBNAME"), sheet.getStr("DATA_PROCEDURE"), sheet.getStr("JE_CORE_EXCELSHEET_ID"), "DATA", "", "", ddSet, new ExcelParamVo(request, results, data, "SHEET_ONE_DATA", sheetOrder, sheetName, nowRow, fieldCodeInfos)));
                            //执行方法插入
                        } else if ("BEAN".equals(clType)) {
                            returnMsgs.add(executeLogic("BEAN", "", "", "", "", "DATA", sheet.getStr("DATA_BEANNAME"), sheet.getStr("DATA_DBNAME"), ddSet, new ExcelParamVo(request, results, data, "SHEET_ONE_DATA", sheetOrder, sheetName, nowRow, fieldCodeInfos)));
                        }
                        nowRow++;
                    }
                }
            }
            String clType = sheet.getStr("AFTER_TYPE");
            if ("PROCEDURE".equals(clType)) {
                int nowRow = startRow + 1;
                for (DynaBean data : datas) {
                    returnMsgs.add(executeLogic("PROCEDURE", "", sheet.getStr("AFTER_DBNAME"), sheet.getStr("AFTER_PROCEDURE"), sheet.getStr("JE_CORE_EXCELSHEET_ID"), "AFTER", "", "", ddSet, new ExcelParamVo(request, results, data, "SHEET_ONE_DATA", sheetOrder, sheetName, nowRow, fieldCodeInfos)));
                    nowRow++;
                }
            } else {
                returnMsgs.add(executeLogic(sheet.getStr("AFTER_TYPE"), sheet.getStr("AFTER_SQL"), sheet.getStr("AFTER_DBNAME"), sheet.getStr("AFTER_PROCEDURE"), sheet.getStr("JE_CORE_EXCELSHEET_ID"), "AFTER", "", "", ddSet, new ExcelParamVo(request, results, datas, sheetOrder, sheetName, "SHEET_AFTER", fieldCodeInfos)));
            }
        }
        if ("BEAN".equals(allType)) {
            returnMsgs.add(executeLogic("BEAN", "", "", "", "", "DATA", group.getStr("AFTER_BEAN"), group.getStr("AFTER_METHOD"), ddSet, new ExcelParamVo(request, results, "ALL_DATA")));
        }
        //全局执行后逻辑
        returnMsgs.add(executeLogic(group.getStr("AFTER_TYPE"), group.getStr("AFTER_SQL"), group.getStr("AFTER_DBNAME"), group.getStr("AFTER_PROCEDURE"), group.getStr("JE_CORE_EXCELGROUP_ID"), "AFTER", group.getStr("AFTER_BEAN"), group.getStr("AFTER_METHOD"), ddSet, new ExcelParamVo(request, results, "ALL_AFTER")));
        return returnMsgs;
    }

}
